package com.hh.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hh.common.exception.ServiceException;
import com.hh.common.utils.AssertUtil;
import com.hh.common.utils.StringUtils;
import com.hh.flow.constant.FlowConstant;
import com.hh.flow.domain.FlowDefinition;
import com.hh.flow.domain.FlowNode;
import com.hh.flow.domain.FlowSkip;
import com.hh.flow.domain.tables.FlowDefinitionTableDef;
import com.hh.flow.enums.NodeType;
import com.hh.flow.enums.PublishStatus;
import com.hh.flow.mapper.FlowDefinitionMapper;
import com.hh.flow.mapper.FlowNodeMapper;
import com.hh.flow.service.IFlowDefinitionService;
import com.hh.flow.service.IFlowNodeService;
import com.hh.flow.service.IFlowSkipService;
import com.hh.mybatis.service.impl.BaseServiceImpl;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.hh.flow.domain.tables.FlowDefinitionTableDef.flow_definition;
import static com.hh.flow.domain.tables.FlowNodeTableDef.flow_node;
import static com.hh.flow.domain.tables.FlowSkipTableDef.flow_skip;

/**
 * 流程定义Service业务层处理
 *
 * @author hh
 * @date 2023-03-29
 */
@Service
public class FlowDefinitionServiceImpl extends BaseServiceImpl<FlowDefinition> implements IFlowDefinitionService {
    @Resource
    private FlowDefinitionMapper definitionMapper;

    @Resource
    private IFlowNodeService nodeService;

    @Resource
    private IFlowSkipService skipService;

    @Override
    public FlowDefinitionMapper getBaseMapper() {
        return definitionMapper;
    }

    @Override
    public QueryWrapper buildWrapper(FlowDefinition flowDefinition) {
        QueryWrapper query = QueryWrapper.create()
                .where(flow_definition.flow_code.eq(flowDefinition.getFlowCode()))
                .and(flow_definition.flow_name.like(flowDefinition.getFlowName()))
                .and(flow_definition.version.eq(flowDefinition.getVersion()))
                .and(flow_definition.is_publish.eq(flowDefinition.getIsPublish()));
        return query;
    }

    @Override
    public List<FlowDefinition> queryByCodeList(List<String> flowCodeList) {
        QueryWrapper queryWrapper = query().where(flow_definition.flow_code.in(flowCodeList));
        return list(queryWrapper);
    }

    @Override
    public void closeFlowByCodeList(List<String> flowCodeList) {
        FlowDefinition flowDefinition = new FlowDefinition();
        flowDefinition.setIsPublish(9);
        update(flowDefinition, flow_definition.flow_code.in(flowCodeList));
    }

    @Override
    public void checkSave(FlowDefinition flowDefinition)
    {
        List<String> flowCodeList = Arrays.asList(flowDefinition.getFlowCode());
        List<FlowDefinition> flowDefinitions = queryByCodeList(flowCodeList);
        for (FlowDefinition beforeDefinition : flowDefinitions) {
            if (flowDefinition.getFlowCode().equals(beforeDefinition.getFlowCode()) && flowDefinition.getVersion().equals(beforeDefinition.getVersion())) {
                throw new ServiceException(flowDefinition.getFlowCode() + "(" + flowDefinition.getVersion() + ")" + FlowConstant.ALREADY_EXIST);
            }
        }
    }

    @Override
    public List<FlowNode> getNodeList(Long definitionId)
    {
        QueryWrapper queryWrapper = query().where(flow_node.definition_id.eq(definitionId));
        List<FlowNode> flowNodes = nodeService.list(queryWrapper);
        StringBuffer skipDescribe;
        Map<String, String> nodeMap = flowNodes.stream().collect(Collectors.toMap(FlowNode::getNodeCode, FlowNode::getNodeName));
        for (FlowNode node : flowNodes) {
            skipDescribe = new StringBuffer();
            QueryWrapper wrapper = query().where(flow_skip.node_id.eq(node.getId()));
            List<FlowSkip> skips = skipService.list(wrapper);
            for (FlowSkip flowSkip : skips) {
                skipDescribe.append("【").append(flowSkip.getConditionValue()).append("】").append("后跳转到").append("【")
                        .append(nodeMap.get(flowSkip.getNextNodeCode())).append("】;  ");
            }
            if (StringUtils.isEmpty(skipDescribe.toString())) {
                node.setSkipDescribe("无");
            } else {
                node.setSkipDescribe(skipDescribe.toString());
            }
        }

        return flowNodes;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveNode(@RequestBody List<FlowNode> flowNodeList, Long definitionId)
    {
        List<Integer> nodeTypes = flowNodeList.stream().map(FlowNode::getNodeType).collect(Collectors.toList());
        AssertUtil.isTrue(nodeTypes.contains(NodeType.start.getKey())
                && nodeTypes.contains(NodeType.end.getKey()), "必须有开始结点和结束结点");

        // 校验
        //每一个流程的开始结点个数
        int startNum = 0;
        for (FlowNode flowNode : flowNodeList) {
            AssertUtil.isTrue(StrUtil.isAllNotBlank(flowNode.getNodeCode(), flowNode.getNodeName()), "结点编码和结点名称不能为空");
            AssertUtil.isFalse(ObjectUtil.isNull(flowNode.getNodeType()), "结点类型不能为空");
            if (NodeType.start.getKey().equals(flowNode.getNodeType())) {
                startNum++;
                AssertUtil.isFalse(startNum > 1, FlowConstant.MUL_START_NODE);
            }
        }

        FlowNode nodeQuery = new FlowNode();
        nodeQuery.setDefinitionId(definitionId);
        // 获取原始结点
        QueryWrapper wrapper = query().and(flow_node.definition_id.eq(definitionId));
        List<FlowNode> flowNodesOrig = nodeService.list(wrapper);

        Set<Long> origNodeIds = flowNodesOrig.stream().map(FlowNode::getId).collect(Collectors.toSet());
        Set<Long> tarNodeIds = flowNodeList.stream().map(FlowNode::getId).collect(Collectors.toSet());

        // 新增结点
        List<FlowNode> nodesAdd = new ArrayList<>();
        // 更新结点
        List<FlowNode> nodesUpdate = new ArrayList<>();
        for (FlowNode flowNode : flowNodeList) {
            if (ObjectUtil.isNull(flowNode.getId())) {
                nodesAdd.add(flowNode);
            } else if (origNodeIds.contains(flowNode.getId())) {
                nodesUpdate.add(flowNode);
            }
        }

        // 删除结点
        List<Long> nodesDelete = new ArrayList<>();
        for (Long origNodeId : origNodeIds) {
            if (!tarNodeIds.contains(origNodeId))
            {
                nodesDelete.add(origNodeId);
            }
        }

        //  执行新增
        FlowDefinition flowDefinition = getById(definitionId);
        for (FlowNode flowNode : nodesAdd) {
            flowNode.setDefinitionId(definitionId);
            flowNode.setVersion(flowDefinition.getVersion());
        }

        if (CollUtil.isNotEmpty(nodesAdd)) {
            nodeService.batchInsert(nodesAdd);
        }

        // 执行更新
        if (CollUtil.isNotEmpty(nodesUpdate)) {
            nodeService.batchUpdate(nodesUpdate);
        }

        // 执行删除
        if (CollUtil.isNotEmpty(nodesDelete)) {
            skipService.deleteByNodeIds(nodesDelete);
            nodeService.removeByIds(nodesDelete);
        }
    }

    /**
     * 保存流程跳转
     * @param skipList
     * @param nodeId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSkip(List<FlowSkip> skipList, Long nodeId)
    {
        // 校验
        for (FlowSkip flowSkip : skipList) {
            AssertUtil.isTrue(StrUtil.isAllNotBlank(flowSkip.getNextNodeCode(), flowSkip.getConditionValue())
                    , "下一个流程结点编码和跳转不能为空");
        }

        FlowNode node = nodeService.getById(nodeId);
        if (!NodeType.end.getKey().equals(node.getNodeType())) {
            AssertUtil.isFalse(CollectionUtils.isEmpty(skipList), "开始和中间结点必须有跳转规则");
        }
        //跳转条件的集合
        Set<String> conditionSet = new HashSet<>();
        //目标结点的集合 这两个集合都不能重复
        Set<String> targetSet = new HashSet<>();
        //遍历结点下的跳转条件
        for (int i = 0; i < skipList.size(); i++) {
            FlowSkip skip = skipList.get(i);
            //目标结点
            String target = skip.getNextNodeCode();
            AssertUtil.isBlank(target, FlowConstant.LOST_DEST_NODE);
            AssertUtil.isFalse(targetSet.contains(target), FlowConstant.SAME_DEST_NODE);
            targetSet.add(target);
            String value = skip.getConditionValue();
            AssertUtil.isFalse(conditionSet.contains(value), FlowConstant.SAME_CONDITION_VALUE);
            conditionSet.add(value);

            skip.setNodeId(node.getId());
            skip.setDefinitionId(node.getDefinitionId());
            skip.setNowNodeCode(node.getNodeCode());
        }
        skipService.deleteByNodeId(nodeId);
        skipService.batchInsert(skipList);
    }


    /**
     * 删除流程定义
     * @param ids
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean remove(List<Long> ids)
    {
        nodeService.remove(flow_node.definition_id.in(ids));
        skipService.remove(flow_skip.definition_id.in(ids));
        return removeByIds(ids);
    }

    @Override
    public boolean publish(Long id)
    {
        FlowDefinition definition = getById(id);
        List<String> flowCodeList = Arrays.asList(definition.getFlowCode());
        // 把之前的流程定义改为已失效
        closeFlowByCodeList(flowCodeList);

        FlowDefinition flowDefinition = new FlowDefinition();
        flowDefinition.setId(id);
        flowDefinition.setIsPublish(PublishStatus.PUBLISHED.getKey());
        return updateById(flowDefinition);
    }

    @Override
    public boolean unPublish(Long id)
    {
        FlowDefinition flowDefinition = new FlowDefinition();
        flowDefinition.setId(id);
        flowDefinition.setIsPublish(PublishStatus.UNPUBLISHED.getKey());
        return updateById(flowDefinition);
    }

}
